grammar plp2;

@header
{
	import java.lang.String;
}

@members
{
	boolean hayMain = false;
	TablaSimbolos tablaSimbolos = new TablaSimbolos();
	public void funcionError( String error )
	{
		System.err.println( error );
		System.exit (1);
	}
	
	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}
}

@lexer::members
{
	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}
}

@rulecatch { 
  catch (RecognitionException re) { 
    reportError(re); 
    System.exit(1); 
  } 
} 


/* SINTAX READER */

s
@init {String exit="";}
	: (c {exit += $c.trad;})*
	EOF 
		{
			if (hayMain)
				System.out.println(exit); 
			else
				funcionError("Error 9: 'main' no esta definido");};

c
returns [String trad]
@init {String clase=""; String id=""; trad = "";}
	: CLASS {$trad += $CLASS.text + " "; clase = $CLASS.text;}
	ID 
		{
			$trad += $ID.text + " "; 
			id = $ID.text; 
			if (tablaSimbolos.existe(id))
			{
				funcionError("Error 5 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
								"): '" +$ID.text + "' ya existe en este ambito");					
			}
			tablaSimbolos.nuevoSimbolo(id, Simbolo.CLASE, Simbolo.ENTERO);
			tablaSimbolos.push();
		}
	LLAVEI {$trad += $LLAVEI.text + "\n";}
	d["    "] {$trad += tablaSimbolos.pop("    ");}
	LLAVED {$trad += $LLAVED.text + "\n";};

d [String h]
returns [String trad]
@init {trad = "";}
	: ( v
	| m[h]) *;

v
returns [String trad]
@init {trad = "";}
	: (DOUBLE ID 
		{ 
			if (tablaSimbolos.existe($ID.text))
			{
				funcionError("Error 5 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
								"): '" +$ID.text + "' ya existe en este ambito");
			}
			else
			{
				tablaSimbolos.nuevoSimbolo($ID.text, Simbolo.VAR, Simbolo.REAL); 
			}
		} 
		PYC)
	| (INT ID 		
		{ 
			if (tablaSimbolos.existe($ID.text))
			{
				funcionError("Error 5 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
								"): '" +$ID.text + "' ya existe en este ambito");
			}
			else
			{
				tablaSimbolos.nuevoSimbolo($ID.text, Simbolo.VAR, Simbolo.ENTERO); 
			}
		} PYC);

m [String h]
@init {String trad; trad = "";}
	:
	VOID {trad += $VOID.text + " ";}
	mid 
		{
			trad += $mid.text + " ";
		}
	PARI PARD LLAVEI  
		{
			trad += $PARI.text + $PARD.text + " " +  $LLAVEI.text + "\n";
			tablaSimbolos.push();
		}
	decl[h]
	cuerpo[h] {trad += tablaSimbolos.pop(h+"    ") + $cuerpo.trad;}
	LLAVED 
		{ 
			trad += h + $LLAVED.text + '\n';
			tablaSimbolos.busca($mid.text).setCuerpo(trad);
		};

mid
returns [String trad]
	: (ID 
		{ 
			$trad = $ID.text;
			if (tablaSimbolos.existe($ID.text))
			{
				funcionError("Error 5 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
								"): '" +$ID.text + "' ya existe en este ambito");				
			}
			else
			{
				tablaSimbolos.nuevoSimbolo($ID.text, Simbolo.METODO, Simbolo.ENTERO);
			}	
			$trad = $ID.text;
		})
	| (MAIN 
		{
			if (tablaSimbolos.existe($MAIN.text))
			{
				funcionError("Error 5 (" + $MAIN.getLine() + "," + ($MAIN.getCharPositionInLine()+1) +
								"): '" +$MAIN.text + "' ya existe en este ambito");				
			}
			else
			{
				tablaSimbolos.nuevoSimbolo($MAIN.text, Simbolo.METODO, Simbolo.ENTERO);
			}
			$trad = $MAIN.text; 
			hayMain = true; 
		}) ;

decl [String h]
returns [String trad]
@init {trad = "";}
	: (v { $trad += $v.trad; })* ;

cuerpo [String h]
returns [String trad]
@init {trad = "";}
	: ( instr[h] { trad += h + $instr.trad; } )* ;

instr [String h]
returns [String trad]
@init {trad = ""; String id; Simbolo simbaux;}
	: ID 
		{
			id = $ID.text;

			simbaux = tablaSimbolos.busca($ID.getText()); 
			if ( simbaux == null ) 
			{ 
				funcionError ("Error 6 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
								"): '" + $ID.getText() + "' no ha sido declarado");
			}
		}
	asig[$ID]
	PYC {$trad = h + id + " " + $asig.trad + $PYC.text + "\n";};

asig [Token id]
returns [String trad]
@init {trad = ""; Simbolo simbaux; }
	: ( 
		{ 
			simbaux = tablaSimbolos.busca($id.getText()); 
			if (simbaux.tipoSimbolo == Simbolo.VAR)
			{
				simbaux.setUsado();
			}
			else	
			{
				funcionError ( "Error 7 (" + id.getLine() + "," + (id.getCharPositionInLine()+1) +
								"): '" + id.getText() + "' no es una variable");
			}
		}
		ASIG factor {$trad = $ASIG.text + " " + $factor.trad;})
	| (	{ 
			$trad = "()";
			simbaux = tablaSimbolos.busca($id.getText()); 
			
			if (simbaux.tipoSimbolo == Simbolo.METODO)
			{
				simbaux.setUsado();
			}
			else
			{
				funcionError ("Error 8 (" + id.getLine()+ "," + (id.getCharPositionInLine()+1) +
					"): '" + id.getText() + "' no es un metodo");
			}

		} PARI PARD);

factor
returns [String trad]
@init {Simbolo simbaux; trad = "";}
	: REAL {trad = $REAL.text;}
	| ENTERO {trad = $ENTERO.text;}
	| ID 
		{
			simbaux = tablaSimbolos.busca($ID.getText()); 
			if ( simbaux != null ) 
			{ 
				if (simbaux.tipoSimbolo == Simbolo.VAR)
				{
					simbaux.setUsado();
				}
				else	
				{
					funcionError ( "Error 7 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
									"): '" + $ID.getText() + "' no es una variable");
				}
			}
			else
			{
				funcionError ("Error 6 (" + $ID.getLine() + "," + ($ID.getCharPositionInLine()+1) +
									"): '" + $ID.getText() + "' no ha sido declarado");
			}			
			trad = $ID.text;
		} ;



/* Analizador léxico: */ 


PARI		:		'(';
PARD		:		')';
PYC			:		';';
ASIG		:		'=';
CLASS		:		'class';
DOUBLE		:		'double';
INT			:		'int';
MAIN		:		'main';
VOID		:		'void';
LLAVEI		:	 	'{';
LLAVED		:		'}';
ID      	:    	('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')* ; 
ENTERO     	:    	('0'..'9')+ ; 
REAL		:		('0'..'9')+'.'('0'..'9')+;
NEWLINE		:		('\r'? '\n'|' '|'\t')+  {skip();}; 
COMENTARIO	:	    '/*' .* '*/' {skip();};


